#include "codedblock.hh"
#include "matrix.hh"
#include <click/config.h>
#include <click/straccum.hh>


CLICK_DECLS

/**
 * Allocate storate for block pointers
 */
void Matrix::init(int k)  {
    massert(k > 0, "%d > 0", k);
    if(_k != k) {
        if(_blocks)
            delete [] _blocks; // NOTE: no actual data lost
        _k = k;
        _blocks = new CodedBlock*[_k];
    }
    for(int i = 0; i < _k; ++i) {
        _blocks[i] = 0;
    }
}

/**
 * Delete stored blocks - be careful when sharing blocks.
 */
void Matrix::clear() {
    if(!_blocks)
        return;
    for(int i = 0; i < _k; ++i) {
        if(_blocks[i]) {
            delete _blocks[i];
            _blocks[i] = 0;
        }
    }
    _rank = 0;
}

String Matrix::print() const{
    StringAccum sa;
    for(int i = 0; i < _k; ++i) {
        if(_blocks[i]){
            sa << i << " [ " << _blocks[i]->print() << " ]\n";
        }
    }
    return sa.take_string();
}

/**
 * Prepare the block to be added to the matrix in triangularized form.
 */
bool PartialGEMatrix::partial(CodedBlock *block) const {
    massert(block->k() == _k, "%d == %d", block->k(), _k);

    block->add_all(*this);
    if (block->degree()) {
        block->normalize();
        return true;
    }
    return false;
}

/**
 * Maintains the triangular invariant:
 *   if Matrix[i] = v, then v[j < i] = 0
 */
bool PartialGEMatrix::add(CodedBlock *block) {
    //update_datasize(block->datasize());

    if(!partial(block)) {
        //delete block;
        return false;
    }
    
    Matrix::add(block->first(), block);
    return true;
}

/**
 * Decode a newly arrived block.
 * The triangular invariant + the identity invariant:
 *    if Matrix[i] = v, then Matrix[j != i][i] = 0
 */
bool FullGEMatrix::add(CodedBlock *block) {
    //update_datasize(block->datasize());

    if(!partial(block)) {
        //delete block;
        return false;
    }

    // this is a new value
    int first = block->first();
    for(int i = 0; i < first; ++i) {
        if(_blocks[i]) {
            CodedBlock &cb = *_blocks[i];
            if(cb[first]) {
                cb.add(*block, cb[first]);
                if(cb.degree() == 1) {
                    if(DEBUG)
                        click_chatter("Decoded! %d\n", i);
                }
            }
        }
    }
    Matrix::add(block->first(), block);
    if(block->degree() == 1) {
        if(DEBUG)
            click_chatter("Decoded! %d\n", block->first());
    }
    return true;
}


CLICK_ENDDECLS
ELEMENT_PROVIDES(Matrix)
ELEMENT_PROVIDES(PartialGEMatrix)
ELEMENT_PROVIDES(FullGEMatrix)

